package com.ght.springboot.util;


import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.ss.usermodel.*;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * @notes Excel 导入解析
 * @autho zph
 * @createTime 2017/8/7 20:18
 */
public class ImportExcel {

    /**
     * @param excelInputStream 文件输入流
     * @param tClass           解析参照对象
     * @param valueStartRow    值开始的行
     * @param valueStartColumn 值开始的列
     * @param sheetIndex       工作簿
     * @return 返回 key、value map的list
     * @throws Exception
     */
    public List<Map<String, Object>> parseExcel(InputStream excelInputStream, Class<?> tClass, int valueStartRow,
                                                int valueStartColumn, int sheetIndex) throws Exception {

        //验证输入值，并转化Workbook对象
        Workbook work = this.getWorkbook(excelInputStream);
        //获取指定的工作簿
        Sheet sheet = work.getSheetAt(sheetIndex);
        //通过反射获取到类的属性值
        //String[] fields = getFieldNamesByClass(tClass);
        Field[] fields = tClass.getDeclaredFields();
        //返回结果接收对象
        List<Map<String, Object>> result = new LinkedList<>();
        Map<String, Object> map = new HashMap<>();
        int k = 0;//属性值集合下标
        Row row;//行
        Cell cell;//单元格
        for (int i = valueStartRow; i < sheet.getLastRowNum() + 1; i++) {
            row = sheet.getRow(i);//行
            for (int j = valueStartColumn; j < row.getLastCellNum(); j++) {
                try {
                    cell = row.getCell(j);
                    Object value = getCellValue(cell);
//                    AssertUtil.isTrue(k + 1 <= fields.length, "单元格值的个数，与返回对象的属性个数不符!");
                    map.put(fields[k].getName(), value);
                    k++;
                } catch (Exception e) {
                    throw new RuntimeException("第" + (i+1) + "行,第" + (j+1) + "列的数据有问题，请检查数据格式!");
                }
            }
            result.add(map);
            map = new HashMap<>();
            k = 0;//属性名称的序号
        }
        return result;
    }

    /**
     * @param excelInputStream 文件输入流
     * @param clazz           解析参照对象
     * @param valueStartRow    值开始的行
     * @param valueStartColumn 值开始的列
     * @param sheetIndex       工作簿
     * @return 返回 对象的集合
     * @throws Exception
     */
    public <T> List<T> parseExcelToClass(InputStream excelInputStream, Class<T> clazz, int valueStartRow,
                                         int valueStartColumn, int sheetIndex) throws Exception {

        //验证输入值，并转化Workbook对象
        Workbook work = this.getWorkbook(excelInputStream);
        //获取指定的工作簿
        Sheet sheet = work.getSheetAt(sheetIndex);
        //通过反射获取到类的属性值
        //String[] fields = getFieldNamesByClass(tClass);
        Field[] fields = clazz.getDeclaredFields();
        //返回结果接收对象
        List<Map<String, Class>> result = new LinkedList<>();
        int k = 0;//属性值集合下标
        Row row;//行
        Cell cell;//单元格
        List<T> objects = new LinkedList<T>();
        int fieldLength = fields.length;
        for (int i = valueStartRow; i < sheet.getLastRowNum() +1; i++) {
            row = sheet.getRow(i);//行
            if (row == null) {
                continue;
            }
            T obj = clazz.newInstance();
            for (int j = valueStartColumn; j < fieldLength; j++) {
                try {
                    cell = row.getCell(j);
                    Object value = getCellValue(cell);
//                    AssertUtil.isTrue(k + 1 <= fieldLength, "单元格值的个数，与返回对象的属性个数不符!");
                    Field f = clazz.getDeclaredField(fields[k].getName());
                    f.setAccessible(true);
                    if (value == null || "".equals(value.toString().trim())) {
                        f.set(obj, null);
                    }else if (fields[k].getGenericType().toString().equals(
                            "class java.lang.String")) {
                        f.set(obj, value.toString().trim());
                    } else if (fields[k].getGenericType().toString().equals(
                            "class java.lang.Integer")) {
                        if (value.toString().contains(".")) {
                            f.set(obj, Integer.valueOf(value.toString().trim().substring(0,value.toString().indexOf("."))));
                        } else {
                            f.set(obj, Integer.valueOf(value.toString().trim()));
                        }

                    } else if (fields[k].getGenericType().toString().equals(
                            "class java.lang.Double")) {
                        f.set(obj, Double.valueOf(value.toString().trim()));
                    } else if (fields[k].getGenericType().toString().equals(
                            "class java.math.BigDecimal")) {
                        f.set(obj, new BigDecimal(value.toString().trim()));
                    }
// else if (fields[k].getGenericType().toString().equals(
//                            "class java.util.Date")) {
//                        f.set(obj, DateUtils.stringParseToDate(value.toString().trim()));
//                    }
                    k++;
                } catch (Exception e) {
                    System.err.println(e);
                    throw new RuntimeException("第" + (i+1) + "行,第" + (j+1) + "列的数据有问题，请检查数据格式!");
                }
            }

            boolean flag = false;
            Field[] fieldArr = clazz.getDeclaredFields();
            for (int a=0;a<fieldArr.length;a++){
                Field f = fieldArr[a];
                f.setAccessible(true);
                Object val = f.get(obj);
                if(val!=null && !"".equals(val.toString().trim())){
                    flag = true;
                }
            }
            if(flag){
                objects.add(obj);
                flag = false;
            }
            k = 0;//属性名称的序号
        }

        return objects;
    }
//
//    /**
//     * @param excelInputStream 文件输入流
//     * @param clazz           解析参照对象
//     * @param valueStartRow    值开始的行
//     * @param valueStartColumn 值开始的列
//     * @param valueEndRow      值开始的行
//     * @param sheetIndex       工作簿
//     * @return 返回 对象的集合
//     * @throws Exception
//     */
//    public <T> List<T> parseExcelToClass(InputStream excelInputStream, Class<T> clazz, int valueStartRow,
//                                         int valueStartColumn,int valueEndRow, int sheetIndex) throws Exception {
//
//        //验证输入值，并转化Workbook对象
//        Workbook work = this.getWorkbook(excelInputStream);
//        //获取指定的工作簿
//        Sheet sheet = work.getSheetAt(sheetIndex);
//        //通过反射获取到类的属性值
//        //String[] fields = getFieldNamesByClass(tClass);
//        Field[] fields = clazz.getDeclaredFields();
//        //返回结果接收对象
//        List<Map<String, Class>> result = new LinkedList<>();
//        int k = 0;//属性值集合下标
//        Row row;//行
//        Cell cell;//单元格
//        List<T> objects = new LinkedList<T>();
//        int maxRowNum =0;
//        if(valueEndRow == 0) maxRowNum = sheet.getLastRowNum() +1;
//        else maxRowNum = valueEndRow;
//        int fieldLength = fields.length;
//        for (int i = valueStartRow; i < maxRowNum; i++) {
//            row = sheet.getRow(i);//行
//            if (row == null) {
//                continue;
//            }
//            T obj = clazz.newInstance();
//            for (int j = valueStartColumn; j < fieldLength; j++) {
//                try {
//                    cell = row.getCell(j);
//                    Object value = getCellValue(cell);
//                    AssertUtil.isTrue(k + 1 <= fieldLength, "单元格值的个数，与返回对象的属性个数不符!");
//                    Field f = clazz.getDeclaredField(fields[k].getName());
//                    f.setAccessible(true);
//                    if (value == null || "".equals(value.toString().trim())) {
//                        f.set(obj, null);
//                    }else if (fields[k].getGenericType().toString().equals(
//                            "class java.lang.String")) {
//                        f.set(obj, value.toString().trim());
//                    } else if (fields[k].getGenericType().toString().equals(
//                            "class java.lang.Integer")) {
//                        if (value.toString().contains(".")) {
//                            f.set(obj, Integer.valueOf(value.toString().trim().substring(0,value.toString().indexOf("."))));
//                        } else {
//                            f.set(obj, Integer.valueOf(value.toString().trim()));
//                        }
//
//                    } else if (fields[k].getGenericType().toString().equals(
//                            "class java.lang.Double")) {
//                        f.set(obj, Double.valueOf(value.toString().trim()));
//                    } else if (fields[k].getGenericType().toString().equals(
//                            "class java.math.BigDecimal")) {
//                        f.set(obj, new BigDecimal(value.toString().trim()));
//                    } else if (fields[k].getGenericType().toString().equals(
//                            "class java.util.Date")) {
//                        f.set(obj, DateUtils.stringParseToDate(value.toString().trim()));
//                    }
//                    k++;
//                } catch (Exception e) {
//                    System.err.println(e);
//                    throw BusinessException.create("第" + (i+1) + "行,第" + (j+1) + "列的数据有问题，请检查数据格式!");
//                }
//            }
//
//            boolean flag = false;
//            Field[] fieldArr = clazz.getDeclaredFields();
//            for (int a=0;a<fieldArr.length;a++){
//                Field f = fieldArr[a];
//                f.setAccessible(true);
//                Object val = f.get(obj);
//                if(val!=null && !"".equals(val.toString().trim())){
//                    flag = true;
//                }
//            }
//            if(flag){
//                objects.add(obj);
//                flag = false;
//            }
//            k = 0;//属性名称的序号
//        }
//
//        return objects;
//    }
//
//    /**
//     * @param excelInputStream 文件输入流
//     * @param clazz           解析参照对象
//     * @param valueStartRow    值开始的行
//     * @param valueStartColumn 值开始的列
//     * @param sheetIndex       工作簿
//     * @return 返回 对象的集合
//     * @throws Exception
//     */
//    public <T> List<T> parseExcelToClassByEnd(InputStream excelInputStream, Class<T> clazz, int valueStartRow,
//                                              int valueStartColumn, int valueEndStartRow,int valueEndColumn,int sheetIndex) throws Exception {
//
//        //验证输入值，并转化Workbook对象
//        Workbook work = this.getWorkbook(excelInputStream);
//        //获取指定的工作簿
//        Sheet sheet = work.getSheetAt(sheetIndex);
//        //通过反射获取到类的属性值
//        //String[] fields = getFieldNamesByClass(tClass);
//        Field[] fields = clazz.getDeclaredFields();
//        //返回结果接收对象
//        List<Map<String, Class>> result = new LinkedList<>();
//        int k = 0;//属性值集合下标
//        Row row;//行
//        Cell cell;//单元格
//        List<T> objects = new LinkedList<T>();
//        int fieldLength = fields.length;
//        for (int i = valueStartRow; i < valueEndStartRow + 1; i++) {
//            row = sheet.getRow(i);//行
//            if (row == null) {
//                continue;
//            }
//            T obj = clazz.newInstance();
//            for (int j = valueStartColumn; j < valueEndColumn; j++) {
//                try {
//                    cell = row.getCell(j);
//                    Object value = getCellValue(cell);
//                    AssertUtil.isTrue(k + 1 <= fieldLength, "单元格值的个数，与返回对象的属性个数不符!");
//                    Field f = clazz.getDeclaredField(fields[k].getName());
//                    f.setAccessible(true);
//                    if (value == null || "".equals(value.toString().trim())) {
//                        f.set(obj, null);
//                    }else if (fields[k].getGenericType().toString().equals(
//                            "class java.lang.String")) {
//                        f.set(obj, value.toString().trim());
//                    } else if (fields[k].getGenericType().toString().equals(
//                            "class java.lang.Integer")) {
//                        if (value.toString().contains(".")) {
//                            f.set(obj, Integer.valueOf(value.toString().trim().substring(0,value.toString().indexOf("."))));
//                        } else {
//                            f.set(obj, Integer.valueOf(value.toString().trim()));
//                        }
//
//                    } else if (fields[k].getGenericType().toString().equals(
//                            "class java.lang.Double")) {
//                        f.set(obj, Double.valueOf(value.toString().trim()));
//                    } else if (fields[k].getGenericType().toString().equals(
//                            "class java.math.BigDecimal")) {
//                        f.set(obj, new BigDecimal(value.toString().trim()));
//                    } else if (fields[k].getGenericType().toString().equals(
//                            "class java.util.Date")) {
//                        f.set(obj, DateUtils.stringParseToDate(value.toString().trim()));
//                    }
//                    k++;
//                } catch (Exception e) {
//                    System.err.println(e);
//                    throw BusinessException.create("第" + (i+1) + "行,第" + (j+1) + "列的数据有问题，请检查数据格式!");
//                }
//            }
//
//            boolean flag = false;
//            Field[] fieldArr = clazz.getDeclaredFields();
//            for (int a=0;a<fieldArr.length;a++){
//                Field f = fieldArr[a];
//                f.setAccessible(true);
//                Object val = f.get(obj);
//                if(val!=null && !"".equals(val.toString().trim())){
//                    flag = true;
//                }
//            }
//            if(flag){
//                objects.add(obj);
//                flag = false;
//            }
//            k = 0;//属性名称的序号
//        }
//
//        return objects;
//    }
//
////    /**
////     * 根据类，获取属性集合
////     *
////     * @param tClass
////     * @return
////     */
////    public String[] getFieldNamesByClass(Class<?> tClass) {
////        Assert.notNull(tClass, "错误的类");
////        Field[] fieldList = tClass.getDeclaredFields();
////        String[] fieldNames = new String[fieldList.length];
////        for (int i = 0; i < fieldList.length; i++) {
////            Field fld = fieldList[i];
////            fieldNames[i] = fld.getName();
////        }
////        return fieldNames;
////    }
//
    /**
     * 获取单元格值
     *
     * @param cell
     * @return
     */
    private Object getCellValue(Cell cell) {
        if (ObjectUtils.isEmpty(cell)) {
            return null;
        }
        Object value;
        switch (cell.getCellType()) {
            case HSSFCell.CELL_TYPE_STRING: // 字符串
                value = cell.getStringCellValue();
                break;
            case HSSFCell.CELL_TYPE_NUMERIC: // 数字
                if (HSSFDateUtil.isCellDateFormatted(cell)) {
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    return sdf.format(HSSFDateUtil.getJavaDate(cell.getNumericCellValue()));
                }
                value = cell.getNumericCellValue();
                break;
            case HSSFCell.CELL_TYPE_BOOLEAN: // Boolean
                value = cell.getBooleanCellValue();
                break;
            case HSSFCell.CELL_TYPE_FORMULA: // 公式
                value = cell.getCellFormula();
                break;
            case HSSFCell.CELL_TYPE_BLANK: // 空值
                value = "";
                break;
            case HSSFCell.CELL_TYPE_ERROR: // 故障
                value = "";
                break;
            default:
                value = "ERROR";
                break;
        }
        return value;
    }

    /**
     * 获取文件
     *
     * @param inStr
     * @return
     * @throws Exception
     */
    private Workbook getWorkbook(InputStream inStr) throws Exception {
        Assert.notNull(inStr, "未获取到导入文件的内容!");
        return WorkbookFactory.create(inStr);
    }

}